%skeleton "lalr1.cc"
%require "2.4.1"
%defines
%define parser_class_name "todo_parser"

%code requires {
/*
 * TODO language 
 * Copyright (c) 2010 TODO Team
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include "compiler.h"
#define YYSTYPE todo::TodoData
class todo_driver;
}

// The parsing context.
%parse-param { todo_driver& driver }
%lex-param   { todo_driver& driver }
     
%locations
%initial-action {
// Initialize the initial location.
@$.begin.filename = @$.end.filename = &driver.file;

};

%debug
%error-verbose

%code {
#include "driver.h"
todo::TodoCompiler compiler;
}

%token END  0      "end of file"
%token EQUAL       "="
%token MINUS_EQUAL "-="
%token PLUS_EQUAL  "+="
%token MUL_EQUAL   "*="
%token DIV_EQUAL   "/="
%token MOD_EQUAL   "%="
%token MODULE      "module"
%token IF          "if"
%token ELSE        "else"
%token ELSIF       "elsif"
%token LOOP        "loop"
%token IDENT 	   "identifier"
%token RETURN      "return"
%token BREAK       "break"
%token PRINTLN     "println"
%token PRINT       "print"
%token DEF         "def"
%token CASE        "case"
%token WHEN        "when"
%token OTHERS      "others"
%token STR         "chars"
%token VAR         "var"
%token INTEGER     "int"
%token BOOL        "bool"
%token STRING      "string"
%token DOUBLE      "double"
%token MAP         "map"
%token VECTOR      "vector"
%token NUM_INTEGER "num_integer"
%token NUM_DOUBLE  "num_double"

%left ',';
%left '|';
%left '^';
%left '&';
%left "+=" "-=" "/=" "*=" "%=";
%left '-' '+' '<<';
%left '*' '/' '%';
%right '!';
%right '[';
%left UMINUS;
%nonassoc "=";

%printer    { /* debug_stream() << $$; */ } "identifier"
%destructor { DEL($$); } "identifier"
%%

%start top_statements;

top_statements: { compiler.location = &yylloc; compiler.is_file = driver.input.size() ? 0:1; }
	statement_list { compiler.finalize(); }
;

statement_list:		statement_list statements 
	|	/* empty */
;

statement_list2:		statement_list2 statements_2
	|	/* empty */
;

statements:		module_stmt
	|	statements_2
	|	def_stmt
;

statements_2:	return_stmt
	|	if_stmt
	|	print_stmt
	|	break_stmt
	|	loop_stmt
	|	var_stmt
	|	assign_stmt
	|	case_stmt
	|	func_call
	|	method_call
;

expr:	assign_stmt
	|	expr '-' expr { compiler.subtraction($1, $3, false); $$ = $1; DEL($3); }
	|	expr '+' expr { compiler.addition($1, $3, false); $$ = $1; DEL($3); }
	|	expr '/' expr { compiler.division($1, $3, false); $$ = $1; DEL($3); }
	|	expr '*' expr { compiler.multiplication($1, $3, false); $$ = $1; DEL($3); }
	|	expr '%' expr { compiler.modulo($1, $3, false); $$ = $1; DEL($3); }
	|	expr '<<' expr
	|	expr '|' expr
	|	expr '^' expr
	|	expr '&' expr
	|	'-' expr %prec UMINUS
	|	method_call
	|	"num_double" { $$ = $1; }
	|	"num_integer" { $$ = $1; }
	|	"identifier" { $$ = $1; }
	|	"chars" { $$ = $1; }
	|	func_call { $$ = $1; }
	|	'(' expr ')' { $$ = $2; }
;

func_call:	"identifier" { $$ = compiler.func_call($1); DEL($1); } '(' call_list_args ')' { $$.type = todo::TODO_RET_VALUE; }
;

method_call: "identifier" '.' "identifier" '(' call_list_args ')' { compiler.method_call($1, $3); DEL($1); DEL($3); }
	|	"num_double" '.' "identifier" '(' call_list_args ')' { compiler.method_call($1, $3); DEL($3); }
	|	"num_integer" '.' "identifier" '(' call_list_args ')' { compiler.method_call($1, $3); DEL($3); }
	|	"chars"	'.' "identifier" '(' call_list_args ')' { compiler.method_call($1, $3); DEL($3); }
;

assign_stmt:	"identifier" "=" expr { compiler.change_var($1, $3); DEL($1); DEL($3); }
	|	"identifier" "-=" expr { compiler.subtraction($1, $3, true); $$ = $1; DEL($3); }
	|	"identifier" "+=" expr { compiler.addition($1, $3, true); $$ = $1; DEL($3); }
	|	"identifier" "/=" expr { compiler.division($1, $3, true); $$ = $1; DEL($1); DEL($3); }
	|	"identifier" "*=" expr { compiler.multiplication($1, $3, true); $$ = $1; DEL($3); }
	|	"identifier" "%=" expr { compiler.modulo($1, $3, true); $$ = $1; DEL($3); }
;

opt_elsif_stmt:		opt_elsif_stmt "elsif" '-' expr ':' { compiler.register_elsif($0, $4, $3); }
					statement_list { $$ = $3; }
	|	/* empty */ { $$.type = todo::TODO_UNUSED; }
;

opt_else_stmt:		"else" ':' { compiler.register_else($0, $2); } statement_list { $$ = $2; }
	|	/* empty */ { $$.type = todo::TODO_UNUSED; }
;

if_stmt:	"if" '-' expr ':' { compiler.register_if($$, $3); } statement_list opt_elsif_stmt opt_else_stmt ';'	
		{ compiler.register_endif($8); }
;

return_stmt:	"return" expr { compiler.ret($2); DEL($2); }
	|	"return" ';' { compiler.ret(); }
;

print_list_expr: print_list_expr ',' expr { compiler.print($3); DEL($3); }
	|	expr { compiler.print($1); DEL($1); }
;

println_list_expr: println_list_expr ',' expr { compiler.print($3, true); DEL($3); }
	|	expr { compiler.print($1, true); DEL($1); }
;

print_stmt:		"print" print_list_expr
	|	"println" println_list_expr
;

type: 	"int"
	|	"double"
	|	"bool"
	|	"string"
	|	"vector"
	|	"map"
;

list_var_stmt:	list_var_stmt ',' "identifier" "=" expr { compiler.register_var(compiler.last_type, $3, $5); DEL($3); }
	|	/* empty */
;

var_stmt:	type "identifier" "=" expr { compiler.register_var($1.data.type, $2, $4); DEL($2); } list_var_stmt
	|	type "identifier" { compiler.register_var($1.data.type, $2); DEL($2); }
;

loop_var_stmt:	type "identifier" "=" expr { DEL($2); }
	|	"var" "identifier" "=" expr  { DEL($2); }
;

loop_stmt:	"loop" '-' expr ':' statement_list ';'
	|	"loop" '-' loop_var_stmt ',' expr ',' expr ':' statement_list ';'
	|	"loop" '-' loop_var_stmt ',' expr ':' statement_list ';'
;

call_list_args:	call_list_args ',' expr { compiler.add_func_call_arg($3); DEL($3); }
	|	expr { compiler.add_func_call_arg($1); DEL($1); }
	|	/* empty */
;

case_when_list:	case_when_list "when" expr ':' statement_list ';'
	|	"when" expr ':'
;

case_stmt:	"case" expr ':' case_when_list "others" ':' statement_list ';' ';'
	|	"case" expr ':' case_when_list ';'
;

break_stmt:		"break"
;

def_list_args:	def_list_args ',' type "identifier" { compiler.add_func_arg($3.data.type, $4); DEL($4); }
	|	type "identifier" { compiler.add_func_arg($1.data.type, $2); DEL($2); }
;

def_stmt:	"def" type "identifier" ':' { compiler.register_func($2.data.type, $3); DEL($3); }
				statement_list2 ';' { compiler.end_register_func(); }
	|	"def" "identifier" ':' { compiler.register_func(todo::TODO_NULL, $2); DEL($2); }
				statement_list2 ';' { compiler.end_register_func(); }
	|	"def" type "identifier" { compiler.register_func($2.data.type, $3); DEL($3); }
				'-' def_list_args ':' statement_list2 ';' { compiler.end_register_func(); }
	|	"def" "identifier" { compiler.register_func(todo::TODO_NULL, $2); DEL($2); }
				'-' def_list_args ':' statement_list2 ';' { compiler.end_register_func(); }
;

module_stmt:	"module" "identifier" ':' statement_list ';' { DEL($2); }
;

%%
     
void yy::todo_parser::error(const yy::todo_parser::location_type& line, const std::string& message)
{
	driver.error(line, message);
}
